/** 
 * 
 * Author: rebecca.younes
 * 
 */

import("email.sendEmail");

import("etherpad.collab.collab_server");
import("etherpad.log");
import("etherpad.pad.padutils");
import("etherpad.pro.pro_accounts");
import("etherpad.pro.domains");
import("etherpad.pro.pro_utils");
import("etherpad.utils.*");

import("fastJSON");

import("sqlbase.sqlobj");

//----------------------------------------------------------------
// send online status email
//----------------------------------------------------------------

function onRequest() {
  
  var ONE_MINUTE = 60 * 1000,      
      ONE_HOUR = ONE_MINUTE * 60,
        
      userId = getNumericId(request.params.userId),
      globalPadId = request.params.globalPadId,
      padIdVals = globalPadId.split('$'),
      domainId = padIdVals[0], 
      localPadId = padIdVals[1],
      showButton = false, // should button continue to show in interface?
      sentEmailCount = 0,
       
      now,
      lastOnlineStatusSent,
      timeSinceLastStatusSent, 
      timeToTryAgain,     
      msg, 
      pad, 
      domainEditorIds,
      onlineEditorIds,
      onlineUsers,
      onlineEditors,
      offlineEditorIds,
      sender,
      padLink;
            
  pad = sqlobj.selectSingle('pro_padmeta', { domainId: domainId, localPadId: localPadId });
  
  if ( pad.lastOnlineStatusSentDate ) { 
    now = Date.parse(new Date());
    lastOnlineStatusSent = Date.parse(pad.lastOnlineStatusSentDate);
    timeSinceLastStatusSent = now - lastOnlineStatusSent;
    if ( timeSinceLastStatusSent <= ONE_HOUR  ) {
      timeToTryAgain = Math.round( ( ONE_HOUR - timeSinceLastStatusSent ) / ONE_MINUTE);
      // Keep the button so user can try again.
      // Once we get client working to be time-sensitive, we won't need this:
      // button can disappear now, re-appear again an hour after emails last sent.
      showButton = true;    
      msg =  pad.lastOnlineStatusSentBy === userId ? 'You recently sent your online status to your group. '
                                                   : 'Emails were recently sent by another group member. ';  
      msg += 'Please try again in ' + timeToTryAgain + ' minutes.';
      writeResponse(msg, showButton);
      return true;      
    } 
  }
   
  // Another approach is to have the client send the ids of other
  // online editors from its otherUsersData. Keep in mind that
  // this would exclude the sender, and could therefore be empty.
  onlineUsers = padutils.accessPadLocal(localPadId, function(pad) {
    return collab_server.getConnectedUsers(pad);
  }, 'r');
   
  // Remove admins and facilitators
  onlineEditors = onlineUsers.filter(function(user) {
    return ! (user.isFacilitator || user.isAdmin);
  });
     
  domainEditorIds = pro_accounts.getDomainEditorIds();
      
  // All registered editors are online.
  // This shouldn't happen: the button is hidden if all editors are online.
  if (onlineEditors.length === domainEditorIds.length) {
    showButton = false;
    msg = 'All group members are online. No emails sent.';
    writeResponse(msg, showButton);
    return true;       
  } 
  
  onlineEditorIds = onlineEditors.map(function(editor) {
    return getNumericId(editor.userId); 
  });

  offlineEditorIds = domainEditorIds.filter(function(id) {
    return onlineEditorIds.indexOf(id) === -1;
  });

  padLink = 'http://' + pro_utils.getFullProHost() + '/' + localPadId;

  sender = sqlobj.selectSingle('pro_accounts', { id : userId });
  
  offlineEditorIds.forEach(function(id) {
    var editor = sqlobj.selectSingle('pro_accounts', { id : id });
    // Send email
    if (sendStatusEmail(editor, sender, padLink)) {
      sentEmailCount++;
    }      
  });
           
  if (!sentEmailCount) {
    msg = 'Server error: no emails sent.';
    showButton = true;
  } else {    
    
    // Update lastOnlineStatusSentDate value in database
    pad.lastOnlineStatusSentDate = new Date();  
    pad.lastOnlineStatusSentBy = userId;  
    sqlobj.updateSingle('pro_padmeta', { id : pad.id }, pad); 
       
    // Do we want to update db and send status ok,
    // or not update db and send status not ok, in this case? 
    if (sentEmailCount < offlineEditorIds.length) {
      msg = 'Server error: not all emails could be sent.'
      showButton = false;
    } else {
      msg = intToWords(sentEmailCount) + ' sent.'      
    }
  }  

  sendFacilitatorEmail(sender, sentEmailCount, offlineEditorIds.length, domainId, padLink);
 
  writeResponse(msg, showButton);
  return true;  
}

function sendStatusEmail(editor, sender, padLink) {
  var workspaceName = getWorkspaceName(),
      subj = "[" + workspaceName + "] A member of your group is online",
      fromAddr = pro_utils.getEmailFromAddr(),
      toAddr = editor.email;

  var body = renderTemplateAsString('email/online_status_notification.ejs', {
    recipient: editor.fullName,
    sender: sender.fullName,
    workspaceName: workspaceName,
    padLink: padLink
  });
  
  try {
    sendEmail(toAddr, fromAddr, subj, {}, body);
    return true;
  } catch (ex) {
    log.exception(ex);
    return false;
  }
}

function sendFacilitatorEmail(sender, sentEmailCount, offlineEditorCount, domainId, padLink) {
  
  var facilitator = pro_accounts.getDomainFacilitator(domainId),
      subj = "[" + getWorkspaceName() + "] A participant has sent online status emails",
      fromAddr = pro_utils.getEmailFromAddr(),
      toAddr = facilitator.email,
      body;
      
  body = renderTemplateAsString('email/online_status_facilitator_notification.ejs', {
    recipient: facilitator.fullName,
    sender: sender.fullName,
    padLink: padLink,
    sentEmailCount: sentEmailCount,
    offlineEditorCount: offlineEditorCount,
    project: appjet.config['project']
  });
  
  try {
    sendEmail(toAddr, fromAddr, subj, {}, body);
    return true;
  } catch (ex) {
    log.logException(ex);
    return false;
  }
}


/* Utility functions */

// Convert id sent from client to numeric id used in pro_accounts table.
// E.g., p.42 -> 42
function getNumericId(id) {
  return parseInt(id.match(/\d+/));
}

function writeResponse(msg, showButton) {
  response.write(fastJSON.stringify({ msg: msg, showButton: showButton}));
}

function intToWords(num) {
  if (num === 1) {
    return 'One email';
  } 
  if (num == 2) {
    return 'Two emails';
  }
  return num + ' emails';
}
